/*
 * Copyright (C) 2004 Derek James and Philip Tucker
 * 
 * This file is part of ANJI (Another NEAT Java Implementation).
 * 
 * ANJI is free software; you can redistribute it and/or modify it under the terms of the GNU
 * General Public License as published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with this program; if
 * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 * 02111-1307 USA
 * 
 * created by Philip Tucker
 */
package simmar.neat;

import java.util.Iterator;
import java.util.List;

import org.jgap.BulkFitnessFunction;
import org.jgap.Chromosome;

import com.anji.integration.Activator;
import com.anji.integration.ActivatorTranscriber;
import com.anji.integration.TranscriberException;
import com.anji.util.Configurable;
import com.anji.util.Properties;

/**
 * Fitness function where error is subtracted from max fitness, then squared. Fitness is skewed
 * such that max fitness is <code>MAX_FITNESS</code>. See
 * <code>calculateErrorFitness()</code> for details. This mimics the error function used in <a
 * href="http://nn.cs.utexas.edu/downloads/papers/stanley.ec02.pdf"> Evolving Neural Networks
 * through Augmenting Topologies </a>.
 * 
 * @author Philip Tucker
 * @see com.anji.integration.TargetFitnessFunction
 */
public class NEATMarioFitnessFunction implements BulkFitnessFunction, Configurable {

	private final static String ADJUST_FOR_NETWORK_SIZE_FACTOR_KEY = "fitness.function.adjust.for.network.size.factor";
	
	private final static int MAX_FITNESS = 15000;
	
	private int[] seedArray = {13, 42, 115};

	private float adjustForNetworkSizeFactor = 0.0f;
	
	private ActivatorTranscriber activatorFactory;
	
	/**
	 * @return max fitness value
	 * @see BulkFitnessFunction#getMaxFitnessValue()
	 */
	public int getMaxFitnessValue() {
		return MAX_FITNESS;
	}

	/**
	 * Assigns random fitness for each chromosome between 1 and <code>MAX_FITNESS</code>
	 * inclusive.
	 * 
	 * @param genotypes <code>List</code> contains <code>Chromosome</code> objects
	 */
	final public void evaluate( List genotypes ) {
		if (!NEATGameWrapper.isInitialized()) {
			NEATGameWrapper.initGameWrapper(false);
		}
		
		int counter = 0;
		
		Iterator it = genotypes.iterator();
		while ( it.hasNext() ) {
			Chromosome chrom = (Chromosome) it.next();
			counter++;
			try {
				Activator activator = activatorFactory.newActivator( chrom );
				int rawFitness = NEATGameWrapper.evaluateActivator(activator, seedArray, false);
				System.out.println("Gene " + counter + "'s raw fitness: " + rawFitness);
				chrom.setFitnessValue( rawFitness - (int) ( adjustForNetworkSizeFactor * chrom.size() ) );
			}
			catch ( TranscriberException e ) {
				// Lacking a way to log this
				chrom.setFitnessValue( 1 );
			}
		}
	}

	/**
	 * @see com.anji.util.Configurable#init(com.anji.util.Properties)
	 */
	public void init( Properties props ) throws Exception {
		try {
			activatorFactory = (ActivatorTranscriber) props.singletonObjectProperty( ActivatorTranscriber.class );
			adjustForNetworkSizeFactor = props.getFloatProperty( ADJUST_FOR_NETWORK_SIZE_FACTOR_KEY, 0.0f );
		}
		catch ( Exception e ) {
			throw new IllegalArgumentException( "invalid properties: " + e.getClass().toString() + ": " + e.getMessage() );
		}
	}
}